package com.insight.common.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import com.yuanqiao.insight.common.util.common.TimeUtils;
import io.minio.ObjectStat;
import lombok.extern.slf4j.Slf4j;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.apache.poi.ss.usermodel.Workbook;
import com.insight.common.constant.CommonConstant;
import com.insight.common.constant.DataBaseConstant;
import com.insight.common.constant.FileStorageTypePrefix;
import com.insight.common.exception.JeecgBootException;
import com.insight.common.util.oss.OssBootUtil;
import org.jeecgframework.poi.util.PoiPublicUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Description: 通用工具类，提供文件上传、字符串处理、中文检测等常用方法
 * @Author: 自动补全
 * @Date: 2024-07-01
 */
@Slf4j
@ConditionalOnProperty(name = "jeecg.uploadType")
@Component
public class CommonUtils {

    /**
     * 文件上传根路径
     */
    private static String uploadpath;
    /**
     * 全局上传类型
     */
    private static String globalUploadType;
    /**
     * Minio桶名
     */
    private static String bucketName;
    /**
     * 旧Minio桶名（迁移用）
     */
    private static String oldBucketName;

    @Value(value = "${jeecg.path.upload}")
    public void initUploadpath(String upload) {
        uploadpath = upload;
    }

    @Value(value = "${jeecg.uploadType}")
    public void initUploadType(String type) {
        globalUploadType = type;
    }

    @Value(value = "${jeecg.minio.bucketName}")
    public void initBucketName(String bN) {
        bucketName = bN;
    }

    //仅当存在桶名有变化（迁移）时，此配置项才有作用
    @Value(value = "${jeecg.minio.oldBucketName:}")
    public void initOldBucketName(String obn) {
        oldBucketName = obn;
    }

    /**
     * 中文正则表达式
     */
    private static Pattern ZHONGWEN_PATTERN = Pattern.compile("[\u4e00-\u9fa5]");

    /**
     * 数字正则表达式
     */
    public static final Pattern pattern = Pattern.compile("(\\-|\\+)?\\d+(\\.\\d+)?");

    //todo 这个和上传的功能没区别，但是local模式下简洁了不少，之后还是需要合并成一个的。

    /**
     * 上传在线图片，支持本地、Minio、OSS等多种存储方式
     *
     * @param data       图片字节数组
     * @param basePath   基础路径
     * @param bizPath    业务路径
     * @param uploadType 上传类型（local/minio/oss）
     * @return 数据库存储路径
     */
    public static String uploadOnlineImage(byte[] data, String basePath, String bizPath, String uploadType) {
        if (data == null || basePath == null || bizPath == null || uploadType == null) {
            return null;
        }
        String dbPath = null;
        String fileName = "image" + Math.round(Math.random() * 100000000000L);
        fileName += "." + PoiPublicUtil.getFileExtendName(data);
        try {
            if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) {
                File file = new File(basePath + File.separator + bizPath + File.separator);
                if (!file.exists()) {
                    file.mkdirs();// 创建文件根目录
                }
                String savePath = file.getPath() + File.separator + fileName;
                File savefile = new File(savePath);
                FileCopyUtils.copy(data, savefile);
                dbPath = bizPath + File.separator + fileName;
            } else {
                InputStream in = new ByteArrayInputStream(data);
                String relativePath = bizPath + "/" + fileName;
                if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
                    dbPath = MinioUtil.upload(in, fileName, relativePath, null);
                } else if (CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)) {
                    dbPath = OssBootUtil.upload(in, relativePath);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dbPath;
    }

    /**
     * 判断文件名是否带盘符，重新处理，去除特殊字符和中文转拼音
     *
     * @param fileName 文件名
     * @return 处理后的文件名
     */
    public static String getFileName(String fileName) {
        //判断是否带有盘符信息
        // Check for Unix-style path
        int unixSep = fileName.lastIndexOf('/');
        // Check for Windows-style path
        int winSep = fileName.lastIndexOf('\\');
        // Cut off at latest possible point
        int pos = (winSep > unixSep ? winSep : unixSep);
        if (pos != -1) {
            // Any sort of path separator found...
            fileName = fileName.substring(pos + 1);
        }
        //替换上传文件名字的特殊字符
        fileName = fileName.replace("=", "").replace(",", "").replace("&", "").replace("#", "");
        //替换上传文件名字中的中文
        if (ifContainChinese(fileName)) {
            fileName = PinyinUtil.getPinyin(fileName, StrUtil.EMPTY);
        }
        //替换上传文件名字中的空格
        fileName = fileName.replaceAll("\\s", "");
        return fileName;
    }

    /**
     * 判断字符串是否包含中文字符
     *
     * @param str 待检测字符串
     * @return 是否包含中文
     */
    public static boolean ifContainChinese(String str) {
        if (str.getBytes().length == str.length()) {
            return false;
        } else {
            Matcher m = ZHONGWEN_PATTERN.matcher(str);
            if (m.find()) {
                return true;
            }
            return false;
        }
    }


    /**
     * 统一全局上传
     * 除了知识库或者明确要上传到minio等对象存储中的地方调用这个方法，参数uploadType一定为空
     *
     * @Return: java.lang.String
     */
    public static String upload(MultipartFile file, String bizPath, String uploadType) {
        if (StringUtils.isBlank(uploadType)) {
            uploadType = globalUploadType;
        }
        String url = "";
        if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) {
            url = uploadLocal(file, bizPath);
            url = FileStorageTypePrefix.LOCAL_PREFIX + url;
        } else if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
            url = MinioUtil.upload(file, bizPath);
            String wholeMinioUrl = MinioUtil.getWholeMinioUrl() + bucketName + "/";
            if (url.startsWith(wholeMinioUrl)) {
                url = url.replace(wholeMinioUrl, "");
            }
            url = FileStorageTypePrefix.MINIO_PREFIX + url;
        } else {
            url = OssBootUtil.upload(file, bizPath);
        }
        return url;
    }

    /**
     * 本地文件上传
     *
     * @param file    文件
     * @param bizPath 自定义路径
     * @return
     */
    public static String uploadLocal(MultipartFile file, String bizPath) {
        try {
            String ctxPath = uploadpath;
            // 检查文件根目录是否存在
            File file1 = bizPath.startsWith(uploadpath) ?
                    new File(bizPath + File.separator)
                    : new File(ctxPath + File.separator + bizPath + File.separator);
            if (!file1.exists()) {
                file1.mkdirs();
            }

            //根据原始文件名称构建新文件的名称
            String orgName = file.getOriginalFilename();
            orgName = CommonUtils.getFileName(orgName);
            String fileName = null;
            if (orgName.contains(".")) {
                fileName = orgName.substring(0, orgName.lastIndexOf('.')) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf('.'));
            } else {
                fileName = orgName + "_" + System.currentTimeMillis();
            }

            //根据根目录和新的文件名拼接新文件全路径并创建新文件
            String savePath = file1.getPath() + File.separator + fileName;
            File savefile = new File(savePath);

            //将原始文件内容复制到新文件中
            FileCopyUtils.copy(file.getBytes(), savefile);

            String dbpath = null;
            if (oConvertUtils.isNotEmpty(bizPath)) {
                dbpath = bizPath + File.separator + fileName;
            } else {
                dbpath = fileName;
            }
            if (dbpath.contains("\\")) {
                dbpath = dbpath.replace("\\", "/");
            }
            return dbpath;
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return "";
    }


    /**
     * 当前系统数据库类型
     */
    private static String DB_TYPE = "";

    public static String getDatabaseType() {
        if (oConvertUtils.isNotEmpty(DB_TYPE)) {
            return DB_TYPE;
        }
        DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
        try {
            return getDatabaseTypeByDataSource(dataSource);
        } catch (SQLException e) {
            //e.printStackTrace();
            log.warn(e.getMessage());
            return "";
        }
    }

    /**
     * 获取数据库类型
     *
     * @param dataSource
     * @return
     * @throws SQLException
     */
    private static String getDatabaseTypeByDataSource(DataSource dataSource) throws SQLException {
        if (dataSource == null) {
            throw new IllegalArgumentException("DataSource is null");
        }
        if ("".equals(DB_TYPE)) {
            Connection connection = dataSource.getConnection();
            if (connection == null) {
                throw new SQLException("Connection returned by DataSource is null");
            }
            try {
                DatabaseMetaData md = connection.getMetaData();
                if (md == null) {
                    throw new SQLException("DatabaseMetaData is null");
                }
                String dbType = md.getDatabaseProductName();
                if (dbType == null) {
                    throw new JeecgBootException("数据库产品名称为null");
                }
                dbType = dbType.toLowerCase();
                if (dbType.indexOf("mysql") >= 0) {
                    DB_TYPE = DataBaseConstant.DB_TYPE_MYSQL;
                } else if (dbType.indexOf("oracle") >= 0 || dbType.indexOf("dm") >= 0) {
                    DB_TYPE = DataBaseConstant.DB_TYPE_ORACLE;
                } else if (dbType.indexOf("sqlserver") >= 0 || dbType.indexOf("sql server") >= 0) {
                    DB_TYPE = DataBaseConstant.DB_TYPE_SQLSERVER;
                } else if (dbType.indexOf("postgresql") >= 0) {
                    DB_TYPE = DataBaseConstant.DB_TYPE_POSTGRESQL;
                } else {
                    throw new JeecgBootException("数据库类型:[" + dbType + "]不识别!");
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            } finally {
                connection.close();
            }
        }
        return DB_TYPE;
    }


    static public boolean deleteFile(List<String> files) throws FileSystemException  {
        for (final String file : files) {
            if (StringUtils.isBlank(file)) {
                continue;
            }
            if (file.startsWith("http")) {
                MinioUtil.removeObject(file);
            } else if (file.startsWith(FileStorageTypePrefix.MINIO_PREFIX)) {
                String localFile = file.substring(FileStorageTypePrefix.MINIO_PREFIX.length());
                MinioUtil.removeObjectByRelativePath(localFile);
            } else {
                String processedFile = file;
                if (file.startsWith(FileStorageTypePrefix.LOCAL_PREFIX)) {
                    processedFile = file.substring(FileStorageTypePrefix.LOCAL_PREFIX.length());
                }
                if (!processedFile.startsWith("resource/")) {
                    processedFile = "resource/" + processedFile;
                }
                File filePath = new File(processedFile);
                if (filePath.exists() && filePath.isFile()) {
                    boolean delete = filePath.delete();
                    if (!delete) {
                        log.warn("首次删除文件失败，准备重试: " + filePath.getAbsolutePath());
                        throw new FileSystemException("文件删除失败: ");
                    }
                }
            }
        }
        return true;
    }


    public static Map<String, Object> saveExcel(String fileName, Workbook workbook, String bizPath) {
        Map<String, Object> result = new HashMap<>();
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            workbook.write(bos);

            byte[] bytes = bos.toByteArray();
            InputStream inputStream = new ByteArrayInputStream(bytes);
            MultipartFile file = MultipartFileUtil.inputStreamToMultipartFile(fileName, ContentType.APPLICATION_OCTET_STREAM.toString(), inputStream);
            String path = CommonUtils.upload(file, bizPath, null);
            long size = file.getSize();
            result.put("filePath", path);
            result.put("fileSize", size);
            result.put("originFileName", fileName);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    public static Map<String, Object> saveEncryptedZip(String fileName, Workbook workbook, String bizPath, String passwd) {
        Map<String, Object> result = new HashMap<>();
        bizPath = "download";
        if (workbook == null || fileName == null || fileName.trim().isEmpty()) {
            return result;
        }
        File directory = new File(bizPath + "/zipEncry");
        if (!directory.exists()) {
            directory.mkdirs();
        }

        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            try {
                workbook.write(bos);
            } finally {
                bos.close();
            }
            byte[] bytes = bos.toByteArray();

            ZipParameters parameters = getZipParameters(fileName, passwd);
            String zipFileName = fileName.substring(0, fileName.lastIndexOf('.')) + ".zip";
            String tempStr1 = DateUtils.formatDateHMS(new Date()).replaceAll(":", "");
            List<String> dayStrList = TimeUtils.getDaysBetween(TimeUtils.getBeforeDateStr(tempStr1.split(" ")[0], -2), tempStr1.split(" ")[0]);
            File[] directoryFiles = directory.listFiles();

            if (directoryFiles != null) {
                for (File dirFile : directoryFiles) {
                    if (dirFile != null && dirFile.getName() != null) {
                        String lastPathStr = dirFile.getName();
                        if (!dayStrList.contains(lastPathStr)) {
                            deleteFolders(dirFile.getPath());
                        }
                    }
                }
            }

            String directoryPath = bizPath + "/zipEncry/" + tempStr1.split(" ")[0] + "/";
            File directory1 = new File(directoryPath);
            if (!directory1.exists()) {
                directory1.mkdirs();
            }

            String zipFileUrl = directoryPath + zipFileName;
            log.info("生成的 ZIP 文件路径: " + zipFileUrl);

            ZipFile zipFile = new ZipFile(zipFileUrl);
            zipFile.addStream(new ByteArrayInputStream(bytes), parameters);

            File file = new File(zipFileUrl);
            if (file.exists()) {
                try (FileInputStream fis = new FileInputStream(file)) {
                    MultipartFile zipMultipartFile = MultipartFileUtil.inputStreamToMultipartFile(zipFileName, ContentType.APPLICATION_OCTET_STREAM.toString(), fis);
                    if (zipMultipartFile != null) {
                        String path = CommonUtils.upload(zipMultipartFile, bizPath, null);
                        long size = zipMultipartFile.getSize();

                        result.put("filePath", path);
                        result.put("fileSize", size);
                        result.put("originFileName", zipFileName);
                    }
                }
            }

        } catch (Exception e) {
            log.error("保存并加密 Excel 文件时出错", e);
        }

        return result;
    }


    // 获取 ZIP 参数的方法（包含加密设置）
    private static ZipParameters getZipParameters(String fileName, String passwd) {
        ZipParameters parameters = new ZipParameters();
        // 设置压缩方式
        parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
        // 设置压缩级别
        parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
        // 设置加密方式（标准加密）
        parameters.setSourceExternalStream(true);
        parameters.setFileNameInZip(fileName);
        if (passwd != null && !passwd.isEmpty()) {
            parameters.setEncryptFiles(true);
            // 设置密码加密
            parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD);
            parameters.setPassword(passwd.toCharArray());
        }
        return parameters;
    }

    // 删除文件夹及其内容的方法
    private static void deleteFolders(String folderPath) throws FileSystemException {
        File folder = new File(folderPath);
        if (folder.exists() && folder.isDirectory()) {
            File[] files = folder.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteFolders(file.getPath());
                    } else {
                        boolean delete = file.delete(); // 删除文件
                        if (!delete) {
                            log.warn("无法删除文件：" + file.getAbsolutePath());
                            throw new FileSystemException("文件删除失败: ");
                        }
                    }
                }
            }
            boolean delete = folder.delete();  // 删除空文件夹
            if (!delete) {
                log.warn("无法删除文件夹：" + folder.getAbsolutePath());
                throw new FileSystemException("文件删除失败: ");
            }
        }
    }

    public static void download(HttpServletRequest request, HttpServletResponse response) throws IOException {
        download(null, request, response);
    }

    public static void download(String filename, HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (request == null || response == null) {
            throw new IllegalArgumentException("HttpServletRequest or HttpServletResponse is null");
        }
        String filePath = extractPathFromPattern(request);
        if (filePath == null) {
            response.setStatus(404);
            throw new IllegalArgumentException("文件路径为空");
        }
        if (filePath.startsWith("http")) {
            getFileFromMinioURL(filename, filePath, response);
        } else if (filePath.startsWith(FileStorageTypePrefix.MINIO_PREFIX)) {
            filePath = filePath.substring(FileStorageTypePrefix.MINIO_PREFIX.length());
            getFileFromMinio(filename, filePath, response);
        } else {
            if (filePath.startsWith(FileStorageTypePrefix.LOCAL_PREFIX)) {
                filePath = filePath.substring(FileStorageTypePrefix.LOCAL_PREFIX.length());
            }
            filePath = filePath.replace("..", "");
            if (filePath.endsWith(",")) {
                filePath = filePath.substring(0, filePath.length() - 1);
            }
            String localPath = uploadpath;
            String downloadFilePath = filePath;
            if (localPath != null && !filePath.substring(0, Math.min(filePath.length(), localPath.length())).equals(localPath)) {
                downloadFilePath = localPath + File.separator + filePath;
            }
            File file = new File(downloadFilePath);
            if (!file.exists()) {
                response.setStatus(404);
                throw new IllegalArgumentException("文件不存在..");
            }

            response.setContentType("application/force-download");
            try {
                if (StringUtils.isNotBlank(filename)) {
                    response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(filename, "UTF-8"));
                } else {
                    response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(file.getName(), "UTF-8"));
                }

                try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
                     OutputStream outputStream = response.getOutputStream()) {
                    byte[] buf = new byte[1024];
                    int len;
                    while ((len = inputStream.read(buf)) > 0) {
                        outputStream.write(buf, 0, len);
                    }
                    response.flushBuffer();
                } catch (Exception e) {
                    log.info("文件下载失败" + e.getMessage());
                    throw e;
                }
            } catch (Exception e) {
                log.error("设置响应头失败", e);
                throw new IOException("设置下载响应失败", e);
            }
        }
    }

    public static InputStream getInputStreamFromFile(String filePath) throws IOException {
        if (filePath.startsWith("/sys/common/static/")) {
            filePath = filePath.replaceFirst("/sys/common/static/", "");
        }
        // ISO-8859-1 ==> UTF-8 进行编码转换
        InputStream inputStream;
        if (filePath.startsWith("http")) {
            inputStream = MinioUtil.getInputstreamByMinioURL(filePath);
        } else if (filePath.startsWith(FileStorageTypePrefix.MINIO_PREFIX)) {
            filePath = bucketName + "/" + filePath.substring(FileStorageTypePrefix.MINIO_PREFIX.length());
            inputStream = MinioUtil.getInputstreamByRelativePath(filePath);
        } else {
            if (filePath.startsWith(FileStorageTypePrefix.LOCAL_PREFIX)) {
                filePath = filePath.substring(FileStorageTypePrefix.LOCAL_PREFIX.length());
            }
            filePath = filePath.replace("..", "");
            if (filePath.endsWith(",")) {
                filePath = filePath.substring(0, filePath.length() - 1);
            }
            String localPath = uploadpath;
            String downloadFilePath = filePath;
            if (!filePath.substring(0, localPath.length()).equals(localPath)) {
                downloadFilePath = localPath + File.separator + filePath;
            }
//			//todo 导出加密
//            if(true){
//                downloadFilePath = zipEncryUtils.pdfEncry(downloadFilePath,"123456");
//            }
            File file = new File(downloadFilePath);
            if (!file.exists()) {
                throw new FileNotFoundException("文件不存在: " + downloadFilePath);
            }

            inputStream = Files.newInputStream(file.toPath());
        }
        return inputStream;
    }

    /**
     * 把指定URL后的字符串全部截断当成参数
     * 这么做是为了防止URL中包含中文或者特殊字符（/等）时，匹配不了的问题
     *
     * @param request
     * @return
     */
    private static String extractPathFromPattern(final HttpServletRequest request) {
        String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
        String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
    }


    public static void getFileFromMinio(String filenameFromFront, String filePath, HttpServletResponse response) throws UnsupportedEncodingException {
        if (response == null || filePath == null) {
            return;
        }
        response.setContentType("application/force-download");
        String fileName = StringUtils.isNotBlank(filenameFromFront) ? filenameFromFront : (filePath.lastIndexOf('/') >= 0 ? filePath.substring(filePath.lastIndexOf('/') + 1) : filePath);
        if (fileName == null) {
            fileName = "download";
        }
        response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));

        try (InputStream inputStream = MinioUtil.getInputstreamByRelativePath(filePath);
             OutputStream outputStream = response.getOutputStream()) {
            if (inputStream != null) {
                byte[] buf = new byte[1024];
                int len;
                while ((len = inputStream.read(buf)) > 0) {
                    outputStream.write(buf, 0, len);
                }
            }
            response.flushBuffer();
        } catch (Exception e) {
            log.info("文件下载失败" + e.getMessage());
        }
    }

    public static void getFileFromMinioURL(String filenameFromFront, String fileUrl, HttpServletResponse response) throws IOException {
        if (response == null || fileUrl == null) {
            return;
        }
        response.setContentType("application/force-download");
        String fileName = StringUtils.isNotBlank(filenameFromFront) ? filenameFromFront : (fileUrl.lastIndexOf('/') >= 0 ? fileUrl.substring(fileUrl.lastIndexOf('/') + 1) : fileUrl);
        if (fileName == null) {
            fileName = "download";
        }
        response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));

        try (InputStream inputStream = MinioUtil.getObjectByURL(fileUrl);
             OutputStream outputStream = response.getOutputStream()) {
            if (inputStream != null) {
                byte[] buf = new byte[1024];
                int len;
                while ((len = inputStream.read(buf)) > 0) {
                    outputStream.write(buf, 0, len);
                }
            }
            response.flushBuffer();
        } catch (Exception e) {
            log.info("文件下载失败" + e.getMessage());
        }
    }

    public static Long getFileSize(String filePath) throws IOException {
        long size = 0L;
        if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(globalUploadType)) {
            filePath = filePath.substring(FileStorageTypePrefix.LOCAL_PREFIX.length());
            filePath = filePath.replace("..", "");
            if (filePath.endsWith(",")) {
                filePath = filePath.substring(0, filePath.length() - 1);
            }
            String downloadFilePath = filePath;
            if (!filePath.startsWith(uploadpath)) {
                downloadFilePath = uploadpath + File.separator + filePath;
            }
            File file = new File(downloadFilePath);
            if (!file.exists()) {
                throw new FileNotFoundException("文件不存在: " + downloadFilePath);
            }
            size = file.length();
        } else if (CommonConstant.UPLOAD_TYPE_MINIO.equals(globalUploadType)) {
            filePath = filePath.substring(FileStorageTypePrefix.MINIO_PREFIX.length());
            try {
                ObjectStat objectInfo = MinioUtil.getObjectInfo(filePath);
                size = objectInfo.length();
            } catch (Exception e) {
                throw new IOException("获取MinIO文件信息失败: " + filePath, e);
            }
        }
        return size;
    }

    public static String stringifyException(final Throwable e) {
        if (e == null) {
            return "(nothing throwable)";
        }
        try {
            StringWriter stm = new StringWriter();
            PrintWriter wrt = new PrintWriter(stm);
            e.printStackTrace(wrt);
            wrt.close();
            String result = stm.toString();
            return result != null ? result : "(null exception string)";
        } catch (Throwable t) {
            return e.getClass().getName() + " (error while printing stack trace)";
        }
    }

    /**
     * 通过正则表达式判断字符串是否为数字
     *
     * @param str
     * @return
     */
    public static boolean isNumber(String str) {
        // 通过 -?[0-9]+(\\\\.[0-9]+)? 进行匹配是否为数字
        return pattern.matcher(str).matches();
    }

}